package com.thd.springboottest.transactional.service.impl;

import com.thd.springboottest.transactional.service.MyUserService;
import com.thd.springboottest.transactional.service.SysUserService;
import com.thd.springboottest.transactional.service.TestService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;

/**
 * com.thd.springboottest.transactional.service.impl.TestServiceImpl
 *
 * @author: wanglei62
 * @DATE: 2019/11/28 10:31
 **/
@Service
public class TestServiceImpl implements TestService {
    @Autowired
    private MyUserService myUserService;
    @Autowired
    private SysUserService sysUserService;


    @Autowired
    private PlatformTransactionManager platformTransactionManager;
    @Autowired
    private DataSourceTransactionManager dataSourceTransactionManager;
    @Autowired
    private TransactionDefinition transactionDefinition;// 事务的一些基础信息，如超时时间、隔离级别、传播属性等





    @Transactional
    public void deleteAll(){
        this.myUserService.deleteAllMyUser();
        this.sysUserService.deleteAllSysUser();
    };

    @Transactional
    public void insertA(boolean hasEx){
        this.myUserService.insertAwithReqTx("张三");
        this.myUserService.insertAwithReqTx("李四");
        this.myUserService.insertAwithReqTx("王五");
        if(hasEx){
            throw new RuntimeException("抛出业务异常!");
        }
        this.sysUserService.insertBwithReqTx("李四");
        this.sysUserService.insertBwithReqTx("张三");
        this.sysUserService.insertBwithReqTx("王五");
    };

    public void insertB(boolean hasEx){
        this.myUserService.insertAwithReqTx("张三");
        this.myUserService.insertAwithReqTx("李四");
        this.myUserService.insertAwithReqTx("王五");
        if(hasEx){
            throw new RuntimeException("抛出业务异常!");
        }
        this.sysUserService.insertBwithReqTx("李四");
        this.sysUserService.insertBwithReqTx("张三");
        this.sysUserService.insertBwithReqTx("王五");
    };

    @Transactional
    public void insertC(boolean hasEx){
        this.myUserService.insertAwithNoTx("张三");
        this.myUserService.insertAwithNoTx("李四");
        this.myUserService.insertAwithNoTx("王五");
        if(hasEx){
            throw new RuntimeException("抛出业务异常!");
        }
        this.sysUserService.insertBwithNoTx("李四");
        this.sysUserService.insertBwithNoTx("张三");
        this.sysUserService.insertBwithNoTx("王五");
    };

    @Transactional
    public void insertD(boolean hasEx){
        this.myUserService.insertAwithNewTx("张三");
        this.myUserService.insertAwithNewTx("李四");
        this.myUserService.insertAwithNewTx("王五");

        this.sysUserService.insertBwithReqTx("李四");
        this.sysUserService.insertBwithReqTx("张三");
        this.sysUserService.insertBwithReqTx("王五");

        if(hasEx){
            throw new RuntimeException("抛出业务异常!");
        }
    };


    @Transactional
    public void insertE(boolean hasEx){
        this.myUserService.insertNestA(hasEx);
    };

    /**
     * 外层方法和内层方法都加了  @Transactional(propagation = Propagation.REQUIRED)
     * 也就是说内存事务和外层事务是一个事务
     *
     * 内层方法抛出异常被外层捕获
     * 但内存方法没有catch，所以内层方法抛出异常，事务被记录为回滚
     * 但执行完外层方法并没有抛出异常(因为内层方法抛出异常被外层捕获了)，所以正常提交事务
     * 但该事物被标记为rollback,所以最终事务回滚，且抛出异常 Transaction rolled back because it has been marked as rollback-only
     *
     * 也就是说Spring提交被标记为rollback的事务时会抛出异常。
     */
    @Transactional(propagation = Propagation.REQUIRED)
    public void testRollbackError(){
        try{
            this.sysUserService.insertWithReqTxError("zhangsan");
        }catch (Exception e){
            System.out.println("抛出异常");
        }

    };

    @Transactional
    public void insertF(boolean hasEx){
        this.myUserService.insertNestAGetBean(hasEx);
    };

    // 测试手工事务
    public void insertG(){
        //手动开启事务！
        TransactionStatus transactionStatus = platformTransactionManager.getTransaction(transactionDefinition);
        /* 业务代码 */
        try{
            this.sysUserService.insertBwithNoTx("李四");
            //提交
            platformTransactionManager.commit(transactionStatus);
            System.out.println("提交");
        }catch(Exception e ){
            //最好是放在catch 里面,防止程序异常而事务一直卡在哪里未提交
//            platformTransactionManager.rollback(transactionStatus);

            // 如果方法上没有@Transactional 则会报错：No transaction aspect-managed TransactionStatus in scope
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            System.out.println("回滚");
        }

        this.myUserService.insertAwithNoTx("sss");
        platformTransactionManager.commit(transactionStatus);



    };


    // 测试手工事务
    @Transactional
    public void insertH(){
        //手动开启事务！
        TransactionStatus transactionStatus = platformTransactionManager.getTransaction(transactionDefinition);
        /* 业务代码 */
        try{
            this.sysUserService.insertBwithNoTx("李四");
            //提交
            platformTransactionManager.commit(transactionStatus);

            System.out.println("提交");
        }catch(Exception e ){
            //最好是放在catch 里面,防止程序异常而事务一直卡在哪里未提交
            platformTransactionManager.rollback(transactionStatus);

            // 如果在@Transactional里，手动提交事务
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
            System.out.println("回滚");
        }
    };


    public void manualBeginTx(){
        //手动开启事务！
        TransactionStatus transactionStatus = platformTransactionManager.getTransaction(transactionDefinition);
        try{
            this.sysUserService.insertBwithNoTx("张三"); // 该方法没有加事务注解
            this.myUserService.insertAwithNoTx("李四");// 该方法没有加事务注解

            // 手工提交事务
            platformTransactionManager.commit(transactionStatus);

            // 回滚事务 如果回滚两个表都不会插入数据
//            platformTransactionManager.rollback(transactionStatus);

        }catch(Exception e){
            // 手工回滚事务
            platformTransactionManager.rollback(transactionStatus);
        }
    }

    /**
     * 手工事务测试
     * 说明： 结合每一个保存代码打上断点，然后用SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCKS; 查看数据库中的锁情况
     */
    public void manualBeginTxMixAutoTx(){
        //手动开启事务！
        TransactionStatus transactionStatus = platformTransactionManager.getTransaction(transactionDefinition);
        try{
            // 方法1  该方法没有加事务注解
            this.myUserService.insertAwithNoTx("张三");// 该方法没有加事务注解

            // 方法2  该方法有加事务注解 传播机制是new , 因为无论是否存在事务都会开启一个事务,所以后续外层事务无论提交还是回滚,都会插入数据
            this.myUserService.insertAwithNewTx("李四");// 该方法有加事务注解 required

            // 方法3  该方法有加事务注解 传播机制是required , 因为是required类型的事务，所以会加入到外层事务中,随着外层事务提交会回滚
            this.myUserService.insertAwithReqTx("王五");// 该方法有加事务注解 new



            // 回滚事务 如果回滚则 无事务的方法没有插入，newTx的方法插入了 , reqTx的方法没有插入
            platformTransactionManager.rollback(transactionStatus);

        }catch(Exception e){
            // 手工回滚事务
            platformTransactionManager.rollback(transactionStatus);
        }
    }




}
